在此我们注意到,因为子模板中没有定义sidebar这个block,所以来自父模板的内容被显示出来,而没有被子模板替代。位于父模板中的{% block %}标签是默认值,如果没有被子模板重写覆盖,它将作为默认值使用。
你可以根据你的需要进行多层继承。 Symfony2项目中一般使用一种三层继承模式来组织模板和页面。当我们使用模板继承时,需要注意:
如果在模板中使用{% extends %},那么它必须是模板的第一个标签。
你基础模板中{% block %}越多越好,记住,子模板不必等一父模板中所有的block。你父模板中block定义的越多,你的布局就越灵活。
如果你发现在多个模板中有重复的内容,这可能就意味着你需要为该内容在父模板中定义一个{% block %}。有些时候更好的解决方案可能是把这些内容放到一个新模板中,然后在该模板中include它。
如果你需要从父模板中获取一个block的内容,你可以使用{{ parent() }}函数。
Table of Contents
...
{{ parent() }}
{% endblock %}
模板的命名和存储位置
默认情况下,模板可以被保存到两个位置:
Symfony2使用bundle:controller:template 字符串语法表示模板。这可以表示许多不同类型的模板,每种都存放在特定的路径下:
AcmeBlogBundle:Blog:index.html.twig 用于指定一个特定页面的模板。
AcmeBlogBundle 表示bundle,说明模板位于AcmeBlogBundle,比如src/Acme/BlogBundle。
Blog 表示BlogController,指定模板位于Resourcs/views的Blog子目录中,index.html.twig为模板名字。
假设AcmeBlogBundle位于src/Acme/BlogBundle,最终的路径将是:src/Acme/BlogBundle/Resources/views/Blog/index.html.twig
AcmeBlogBundle::layout.html.twig 该表示法指向AcmeBlogBundle的基模板。没有controller部分,所以模板应该位于AcmeBlogBundle的Resources/views/layout.html.twig
::base.html.twig 表示一个应用程序级的基模板或者布局文件。注意,该语句两个冒号开头,意味着没有bundle和controller部分,这说明该文件不在某个bundle中,而是位于根目录下 app/Resources/views
在重写Bundle模板一节,你将发现位于AcmeBlogBundle的模板是如何被位于app/Resources/AcmeBlogBundle/views/目录下的所有模板同名重写的,
这种方式给了我们一个有力的途径来重写供应商提供的bundle的模板。
模板后缀(suffix)
bundle:controller:template 句法说明了每个模板文件的存放位置。每个模板名字也有两个扩展名来指定格式和模板引擎。
AcmeBlogBundle:Blog:index.html.twig HTML格式,Twig引擎
AcmeBlogBundle:Blog:index.html.php HTML格式,PHP引擎
AcmeBlogBundle:Blog:index.css.twig CSS格式,Twig引擎
默认情况下,Symfony2的任何模板都可以被写成Twig或者PHP引擎的,它由后缀决定。其中后缀的前一部分(.html,.css)表示最终生成的格式。
标签和助手类
你已经基本了解了模板,它们如何命名如何使用模板继承等。最难理解的部分已经过去。接下来我们将了解大量的可用工具来帮助我们执行最通用的模板任务比如包含另外一个模板,链接一个页面或者包含一个图片等。
Symfony2 中包含了血多序列化的Twig标签和功能函数来帮助设计者更容易的完成工作。在PHP中,模板系统提供了一个可扩展的helper系统,它可以在模板上下文中提供许多有用的内容。我们已经看过一些内建的Twig标签,比如{% block %}{% extends %}等,还有PHP 助手$view['slots']。
包含其它模板:
你可能经常想在多个不同的页面中包含同一个模板或者代码片段。比如一个应用程序中有"新闻文章",模板代码在显示一片文章时可能用到文章详细页面,一个现实最流行文章的页面,或者一个最新文章的列表页面等。
当你需要重用一些PHP代码,你通常都是把这些代码放到一个PHP类或者函数中。同样在模板中你也可以这么做。通过把可重用的代码放到一个它自己的模板中,然后把这个模板包含到其他模板中。比如我们创建一个可重用模板如下:
Twig格式:
{{ article.title }}
{{ article.body }}
PHP代码格式:
getTitle() ?>
getAuthorName() ?>
getBody() ?>
然后我们把它包含到其它模板定义中:
Twig格式:
Recent Articles
{% for article in articles %}
{% include 'AcmeArticleBundle:Article:articleDetails.html.twig' with {'article': article} %}
{% endfor %}
{% endblock %}
PHP代码格式:
extend('AcmeArticleBundle::layout.html.php') ?>
start('body') ?>
Recent Articles
render('AcmeArticleBundle:Article:articleDetails.html.php',array('article' => $article)) ?>
stop() ?>
模板的包含使用{% include %}标签。模板的名称要使用通用方式。在articleDetails.html.twig模板中使用article变量,这里通过在list.html.twig模板中使用with命令传入。{'article':article}语法是标准Twig哈希映射的写法。如果我们需要传递多个元素,可以写成{'foo': foo,'bar': bar}。
嵌入Controllers
有些情况下,你需要比包含简单模板做到更多。假设你有一个菜单栏sidebar在你的布局文件中来显示最新的文章。获取三篇最新文章可能需要查询数据库或者执行其它包含很多逻辑的操作,这样就不能在一个模板中进行了。这种情况的解决方案是简答键入一个完整的controller到你的模板。首先创建一个controller来渲染特定数量的最近文章:
render('AcmeArticleBundle:Article:recentList.html.twig',array('articles'=>articles));
}
}
而recentList模板则相当简单:
Twig格式:
{% endfor %}
PHP;">
{# app/Resources/views/base.html.twig #}
...
PHP代码格式:
PHP;">
...
无论什么时候,你需要一个变量或者一些列信息时,你不必在模板中访问,而是考虑渲染一个controller。因为Controller能够更快的执行并且很好的提高了代码的组织和重用。
在你的应用程序中创建一个链接到其它页面对于一个模板来说是再普通不过的事情了。我们采用path Twig函数基于路由配置来生成URL而非在模板中硬编码URL。以后如果你想修改一个特定页面的URL,你只需要改变路由配置即可,模板将自动生成新的URL。比如我们打算链接到"_welcome"页面,首先定义其路由配置:
YAML格式:
XML格式:
PHP;">
PHP代码格式:
add('_welcome',new Route('/',array(
'_controller' => 'AcmeDemoBundle:Welcome:index',)));
return $collection;
我们可以在模板中使用Twig函数 path来引用这个路由链接该页面。
Twig格式:
PHP代码格式:
上面的内容会生成一个URL /
我们看另一个复杂一些的路由定义:
YAML格式:
PHP;">
article_show:
pattern: /article/{slug}
defaults: { _controller: AcmeArticleBundle:Article:show }
XML格式:
PHP代码格式:
add('article_show',new Route('/article/{slug}',array(
'_controller' => 'AcmeArticleBundle:Article:show',)));
return $collection;
这种情况下你需要指定路由名称(article_show)还要一个参数值{slug}。现在让我们再来看上面的recentList模板 ,我们修改以前的硬编码而使用path来链接正确的文章。
Twig格式:
PHP;">
{# src/Acme/ArticleBundle/Resources/views/Article/recentList.html.twig #}
{% for article in articles %}
{% endfor %}
PHP代码格式:
你也可以通过url Twig函数来生成一个绝对路径的URL:
在PHP代码模板中,你需要给generate()方法传入第三个参数true 来实现生成绝对路径URL
模板通常也需要一些图片,Javascript,样式文件和其它资产。当然你可以硬编码它们的路径。比如/images/logo.png。 但是Symfony2 提供了一个更加动态的Twig函数 asset()。
Twig格式:
PHP;">
PHP代码格式:
PHP;">

logo.png') ?>" alt="Symfony!" />
asset函数的主要墨笔是让你的应用程序更加轻便。如果你的应用程序在你的主机根目录下(比如:http://example.com),那么它会渲染出的路径为 /images/logo.png 。但是如果你的应用程序位于一个子目录中(比如:http://example.com/my_app),这时它会为你渲染出的路径变为 /my_app/images/logo.png 。asset函数能够根据你的应用程序来生成正确的路径。
另外,如果你使用asset函数,symfony可以自动追加一个查询字符串到你的资产,以保证被更新的静态资源不会在部署时被缓存。比如:/images/logo.png 可能看起来是 /images/logo.png?v2 。
包含样式表和Javascript文件到Twig模板:
每个网站中都不可能缺少了样式表和javascript文件。在Symfony中,这些内容可以通过模板继承很好的进行包含处理。Symfony打包了另外一个类库叫做Assetic,它允许你对这些资源做更多的有趣操作。首先在你的基模板中添加两个blocks来保存你的资源,一个叫stylesheets,放在head标签里,另一个叫javascript,放在body结束标签上面一行。这些blocks将包含你整个站点所需要的所有stylesheets和javascripts。
PHP;">
{# 'app/Resources/views/base.html.twig' #}
{# ... #}
{% block stylesheets %}
{% endblock %}
{# ... #}
{% block javascripts %}
没有任何的输出安全转义,结果模板会触发JavaScript弹出框:
alert('hello!')
这种情况看上去,没多大害处,如果一个用户知道这一步,它完全有能力写一个javascript在我们未知的安全区域来执行一些恶意行为。这个问题的解决办法是输出安全转义。 如果添加了输出安全转义,同样的模板会渲染出无害的内容,script标签会作为普通文本输出到屏幕上。
alert('helloe')
PHP和Twig模板化系统采用了不同的方式来解决这个问题。如果你使用Twig,默认情况下是输出安全转义的,你的输出是受到保护的。如果是PHP,则输出安全转义不是自动的,需要你手工的进行处理。
Twig中的安全输出
如果你使用Twig模板,那么输出安全是默认的。你不需要对用户提交的输出内容进行手动保护。在某些情况下,你需要关闭输出安全保护,当你渲染某个可信变量或者包含某个标签时。假设管理员用户可以编写一些代码有HTML标签的文章。默认情况下,Twig将转义文章体。为了渲染正常,需要添加一个raw 过滤器:
PHP;">
{{article.body | raw }}
你也可以在{% block %}区域或者整个模板中关闭安全保护。
在PHP中安全输出保护不是自动完成的,这就意味着除非你显示的选择来对某个输出变量进行保护,否则输出都是不安全的。我们使用view的方法escape()来对输出变量进行安全输出保护:
PHP;">
Hello escape($name) ?>
默认情况下,escape()方法默认情况下假设变量是被渲染在一个HTML上下文中的。也就是说只针对HTML安全。 它的第二个参数让你能够改变它针对的上下文。
比如需要在javascript上下文中输出某些东西,就是用 js 上下文。
PHP;">
var myMsg = 'Hello escape($name,'js') ?>';
模板调试
当使用PHP代码时,我们可以使用var_dump()来快速的查看一个值的变量传递。同样在Twig中,我们可以使用debug扩展来达到相同的效果,只是需要预先在配置文件中开启。
YAML 格式:
XML格式:
PHP代码格式:
addTag('twig.extension');
$container->setDefinition('acme_hello.twig.extension.debug',$definition);
这时候,我们就可以使用dump函数来查看模板参数了:
PHP;">
{# src/Acme/ArticleBundle/Resources/views/Article/recentList.html.twig #}
{{ dump(articles) }}
{% for article in articles %}
{% endfor %}
模板格式:
模板是一个渲染任何格式内容的通用的方式。大多数情况下,我们使用模板来渲染HTML内容。模板同样也能渲染想javascript,CSS,XML以及你能想象到的其它格式内容。比如,同一个资源resource经常被渲染为不同的格式。把文章目录页渲染为XML,你需要在模板的名称中包含相应的格式即可。
XML 模板名: AcmeArticleBundle:Article:index.xml.twig
XML 模板文件名:index.xml.twig
事实上,这里只是命名上有了变化,而模板并没有真正的基于不同的格式渲染不同。在大多数情况下,你可能想让单一的controller根据请求的格式不同渲染多个不同的格式。下面是一个通常的写法:
getRequest()->getRequestFormat();
return $this->render('AcmeBlogBundle:Blog:index.'.$format.'.twig');
}
Request对象的getRequestFormat()方法默认返回值为html,request的格式通常是在路由时决定的。比如/contact 设置的请求格式是html,而 /contact.xml 设置的请求格式则是 XML。创建一个包含请求格式的链接,只需要在参数哈希表中包含 _format键值即可。
Twig格式:
PHP代码格式:
总结思考:
Symfony2中的模板引擎是一个强大的工具,你可以用它来根据需要生成包括HTML,XML以及其它任何格式的内容。尽管模板时controller生成内容的通常方式,但是不是必须的。controller返回的Response对象可以使用模板也可以没有模板。
render('AcmeArticleBundle:Article:index.html.twig');
// 使用简单文本
内容生成Response对象
$response = new Response('response content');
Symfony2的模板引起非诚灵活,默认情况下支持传统的PHP模板和圆滑强大的Twig模板,他们都拥有非常丰富的帮助函数来执行一些常见任务,Symfony2推荐使用Twig模板,因为它更加简洁,高效,能更好的处理继承等。
希望本文所述对大家基于Symfony框架的PHP程序设计有所帮助。