视图

3.3 version
维护中的版本

读过本教程第一部分之后,你已经决定继续深入,再多学10分钟。在第二部分,你主要了解Twig,一个快速、可定制而且安全的PHP模板引擎。Twig令你的模板更加可读、更加聪明;它与web界面设计师更加友好。

开始熟悉Twig 

官方文档是学习Twig引擎每一处细节的最佳资源。本小节仅针对主要概念提供一个快速概览。

一个twig模板,是指一个可以生成做任意类型(HTML, CSS, JavaScript, XML, CSV, LaTeX等)内容的文本文件。twig元素被从模板中的上面内容中分离出来,使用下述定界符:

{{ ... }} 输出变量内容,或执行表达式给出结果

{% ... %} 控制模板逻辑,比如,它常被用于执行for循环以及if声明等。

{# ... #} 在模板文件中包容注释内容。与HTML注释相反,twig注释不出现在渲染之后的模板中。

下面的小模板描述了一些基本功能,它有两个变量page_titlenavigation,它们被传到模板中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!DOCTYPE html>
<html>
    <head>
        <title>{{ page_title }}</title>
    </head>
    <body>
        <h1>{{ page_title }}</h1>
 
        <ul id="navigation">
            {% for item in navigation %}
                <li><a href="{{ item.url }}">{{ item.label }}</a></li>
            {% endfor %}
        </ul>
    </body>
</html>

为了渲染模板,Symfony在控制器中使用render()方法。如果模板需要一些变量来生成内容,那么render方法的第二个参数,就是用来接受这些变量的数组。

1
2
3
$this->render('default/index.html.twig', array(
    'variable_name' => 'variable_value',
));

传给模板的变量可以是字符串、数组或对象。twig抽象出了它们的区别,允许你使用“点”(.)操作符来使用这些变量的“属性”(attributes)。下例显示了如何依托变量类型,来显示控制器传递的变量之内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{# 1. Simple variables 简单变量 #}
{# $this->render('template.html.twig', array(
       'name' => 'Fabien')
   ) #}
{{ name }}
 
{# 2. Arrays 数组 #}
{# $this->render('template.html.twig', array(
       'user' => array('name' => 'Fabien'))
   ) #}
{{ user.name }}
 
{# alternative syntax for arrays 数组的又一写法 #}
{{ user['name'] }}
 
{# 3. Objects 对象 #}
{# $this->render('template.html.twig', array(
       'user' => new User('Fabien'))
   ) #}
{{ user.name }}
{{ user.getName }}
 
{# alternative syntax for objects 对象的又一写法 #}
{{ user.name() }}
{{ user.getName() }}

装饰模板 

经常面对的一件事是,项目中的模板需要共享常规元素,比如为人熟知的通用头或通用脚。twig解决此类问题时,巧用了“模板继承”的概念。此功能允许你创建一个“基础模板(base template)”,它包含了所有网站常规元素,然后再通过定义“区块(block)”来保证子模板可以覆写其中的内容。

index.html.twig使用了extends标签,来表明它是继承自base.html.twig模板:

1
2
3
4
5
6
{# app/Resources/views/default/index.html.twig #}
{% extends 'base.html.twig' %}
 
{% block body %}
    <h1>Welcome to Symfony!</h1>
{% endblock %}

打开app/Resources/views/base.html.twig文件,它对应的就是base.html.twig模板,你可以发现以下Twig代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{# app/Resources/views/base.html.twig #}
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <title>{% block title %}Welcome!{% endblock %}</title>
        {% block stylesheets %}{% endblock %}
        <link rel="icon" type="image/x-icon" href="{{ asset('favicon.ico') }}" />
    </head>
    <body>
        {% block body %}{% endblock %}
        {% block javascripts %}{% endblock %}
    </body>
</html>

{% block %}标签告诉模板引擎,有子模板需要覆写当前模板中的这些区块。 例如,index.html.twig模板就覆写了body区块,但是没有覆写title区块,因此title部分仍将显示定义在base.html.twig基础模板中的默认内容。

使用标签、调节器、函数 

twig模板最有力的功能,是它的可扩展性,通过tag、filter和function。看一眼下面的简单模板,它使用了变量调节,改变了原本要显示给用户的信息。

1
2
3
4
5
6
7
<h1>{{ article.title|capitalize }}</h1>
 
<p>{{ article.content|striptags|slice(0, 255) }} ...</p>
 
<p>Tags: {{ article.tags|sort|join(", ") }}</p>
 
<p>Activate your account before {{ 'next Monday'|date('M j, Y') }}</p>

别忘了查看Twig官方文档,来学习标签、调节器、函数的每一个知识点。

模板包容 

在模板间共享码段的最佳方式就是创建一个小模板,然后用其他模板包容它。

设想,我们需要把广告显示在一些程序页面上。首先,创建一个banner.html.twig模板:

1
2
3
4
{# app/Resources/views/ads/banner.html.twig #}
<div id="ad-banner">
    ...
</div>

为了把这个广告显示在做生意页面,包容banner.html.twig模板即可,使用include()函数来实现:

1
2
3
4
5
6
7
8
{# app/Resources/views/default/index.html.twig #}
{% extends 'base.html.twig' %}
 
{% block body %}
    <h1>Welcome to Symfony!</h1>
 
    {{ include('ads/banner.html.twig') }}
{% endblock %}

内嵌其他控制器 

那么,如果你想把其他控制器中的输出结果引入到当前模板,该怎么办?这在Ajax操作中可是非常有用的。或者,当内嵌模板所需要的变量在当前模板中并不可用时,怎么办?

假设,你创建了topArticlesAction这样一个控制器方法,用于显示网站最受欢迎的文章。如果你需要在index模板中来“渲染出”这个方法的输出结果(通常是一些html内容),那么使用render()方法即可:

1
2
{# app/Resources/views/index.html.twig #}
{{ render(controller('AppBundle:Default:topArticles')) }}

此处的render()controller()函数使用到特殊的AppBundle:Default:topArticles语法,引用了来自Default controller的topArticlesAction方法 (至于AppBundle部分的意义将在后面解释):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// src/AppBundle/Controller/DefaultController.php
class DefaultController extends Controller
{
    public function topArticlesAction()
    {
        // look for the most popular articles in the database
        $articles = ...;
 
        return $this->render('default/top_articles.html.twig', array(
            'articles' => $articles,
        ));
    }
 
    // ...
}

创建分属不同页面的链接 

创建页面间的链接是网络程序必做之事。与在模板中将链接写死不同,twig的path函数知道如何生成基于路由系统的URL。因此,你的所有链接都可以轻易的更新,只需修改路由配置即可:

1
<a href="{{ path('homepage') }}">Return to homepage</a>

path函数的第一个参数接收路由名称(译注:定义在route.yml等路由配置文件中,或annotation中),同时可选第二个参数,用于接收路由所需的参数。

url函数,与path函数很相似,但它生成的是绝对路径的链接,这在我们对外输出电子邮件以及RSS文件时非常好用:<a href="{{ url('homepage') }}">Visit our website</a>

包容asset资源:图片/JS/CSS 

互联网若是没有了图片、js文件和css文件,将会怎样?Symfony提供了asset功能来轻松处理这些资源:

1
2
3
<link href="{{ asset('css/blog.css') }}" rel="stylesheet" type="text/css" />
 
<img src="{{ asset('images/logo.png') }}" />

asset()函数从web/文件夹里寻找网页所需资源。如果你存储相关文件在其他文件夹,参考这篇文章来掌握如何管理web assets。

使用asset()函数,令程序更加便携。这是因为你可以移动程序所在目录到服务器web根目录下的任何地方而毋须更改模板中的任何代码。

视图层总结 

Twig简单而强大。多亏了父模板、区块、文件和action的包容,你可以用合乎逻辑和易于扩展的方式来轻松组织模板。

目前你与Symfony打了20分钟的交道,但你已经用这框架做出了精彩的内容。这就是Symfony的威力。虽然基础知识很容易掌握,但你需要了解的是,这种“简便”是隐藏在一个具有很大灵活性的架构之中。

你需要让自己走得更远,下面是控制器。再来一个10分钟如何?

本文,包括例程代码在内,采用的是 Creative Commons BY-SA 3.0 创作共用授权。

登录symfonychina 发表评论或留下问题(我们会尽量回复)